package vip.xiaomoli.nurse.component;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import org.springframework.util.ClassUtils;
import vip.xiaomoli.nurse.annotation.Inject;
import vip.xiaomoli.nurse.model.Api;
import vip.xiaomoli.nurse.model.FieldWrapper;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

public class InjectResolver {

    public static void resolve(Object res) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        if (null != res) {
            if (res instanceof Collection) {
                Collection collection = (Collection) res;
                for (Object obj : collection) {
                    resolve(obj);
                }
            } else if (res instanceof Map) {
                Map map = (Map) res;
                for (Object key : map.keySet()) {
                    resolve(map.get(key));
                }
            } else {
                Class clazz = res.getClass();
                if (clazz.isArray()) {
                    if (!ClassUtils.isPrimitiveOrWrapper(clazz.getComponentType())) {
                        Object[] objs = (Object[]) res;
                        for (Object obj : objs) {
                            resolve(obj);
                        }
                    }
                } else if (!ClassUtils.isPrimitiveOrWrapper(clazz)) {
                    doResolve(res);
                }
            }
        }
    }

    private static void doResolve(Object res) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field[] fields = res.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Inject ann = field.getAnnotation(Inject.class);
            if (null != ann) {
                Api api = new Api(ann.apiClass(), ann.methodName());
                Object paramValue = getParamValue(ann, res);
                FieldWrapper fieldWrapper = new FieldWrapper(field, res);
                InjectContainer.put(api, paramValue, fieldWrapper);
            } else {
                Object obj = field.get(res);
                resolve(obj);
            }
            field.setAccessible(false);
        }
    }

    private static Object getParamValue(Inject ann, Object res)
            throws IllegalAccessException, InstantiationException {
        Object paramValue;
        String[] paramFieldName = ann.paramFieldName();
        if (1 == paramFieldName.length && !paramFieldName[0].contains(":")) {
            paramValue = ReflectUtil.getFieldValue(res, paramFieldName[0]);
        } else {
            Method method = ReflectUtil.getMethod(ann.apiClass(), ann.methodName());
            Class<?> paramType = method.getParameterTypes()[0];
            Type elementType = ClassUtil.getTypeArgument(paramType);
            paramValue = ((Class) elementType).newInstance();
            for (String pfName : paramFieldName) {
                String[] pfNameArr = pfName.split(":");
                BeanUtil.setFieldValue(
                        paramValue, pfNameArr[0], ReflectUtil.getFieldValue(res, pfNameArr[1])
                );
            }
        }
        return paramValue;
    }

}
